<script type="text/javascript">
  var defaultColors = [ '#000', '#D00', '#00CF12', '#C2CB00', '#3100CA',
  '#E100C6', '#00CBCB', '#C7C7C7', '#686868', '#FF5959', '#00FF6B',
  '#FAFF5C', '#775AFF', '#FF47FE', '#0FF', '#FFF' ];

function term2html(text, options) {
  options = options || {};
  var colors = options.colors || defaultColors;

  // EL – Erase in Line: CSI n K.
  // Erases part of the line. If n is zero (or missing), clear from cursor to
  // the end of the line. If n is one, clear from cursor to beginning of the
  // line. If n is two, clear entire line. Cursor position does not change.
  text = text.replace(/^.*\u001B\[[12]K/mg, '');

  // CHA – Cursor Horizontal Absolute: CSI n G.
  // Moves the cursor to column n.
  text = text.replace(/^(.*)\u001B\[(\d+)G/mg, function(_, text, n) {
    return text.slice(0, n);
  });

  // SGR – Select Graphic Rendition: CSI n m.
  // Sets SGR parameters, including text color. After CSI can be zero or more
  // parameters separated with ;. With no parameters, CSI m is treated as
  // CSI 0 m (reset / normal), which is typical of most of the ANSI escape
  // sequences.
  var state = {
    bg: -1,
    fg: -1,
    bold: false,
    underline: false,
    negative: false
  };
  text = text.replace(/\u001B\[([\d;]+)m([^\u001B]+)/g, function(_, n, text) {
    // Update state according to SGR codes.
    n.split(';').forEach(function(code) {
      code = code | 0;
      if (code === 0) {
        state.bg = -1;
        state.fg = -1;
        state.bold = false;
        state.underline = false;
        state.negative = false;
      } else if (code === 1) {
        state.bold = true;
      } else if (code === 4) {
        state.underline = true;
      } else if (code === 7) {
        state.negative = true;
      } else if (code === 21) {
        state.bold = false;
      } else if (code === 24) {
        state.underline = false;
      } else if (code === 27) {
        state.negative = false;
      } else if (code >= 30 && code <= 37) {
        state.fg = code - 30;
      } else if (code === 39) {
        state.fg = -1;
      } else if (code >= 40 && code <= 47) {
        state.bg = code - 40;
      } else if (code === 49) {
        state.bg = -1;
      } else if (code >= 90 && code <= 97) {
        state.fg = code - 90 + 8;
      } else if (code >= 100 && code <= 107) {
        state.bg = code - 100 + 8;
      }
    });

    // Convert color codes to CSS colors.
    var bold = state.bold * 8;
    var fg, bg;
    if (state.negative) {
      fg = state.bg | bold;
      bg = state.fg;
    } else {
      fg = state.fg | bold;
      bg = state.bg;
    }
    fg = colors[fg] || '';
    bg = colors[bg] || '';

    // Create style element.
    var style = '';
    if (bg) {
      style += 'background-color:' + bg + ';';
    }
    if (fg) {
      style += 'color:' + fg + ';';
    }
    if (bold) {
      style += 'font-weight:bold;';
    }
    if (state.underline) {
      style += 'text-decoration:underline';
    }
    var html = text.
      replace(/&/g, '&amp;').
      replace(/</g, '&lt;').
      replace(/>/g, '&gt;');

    // Return HTML for this section of formatted text.
    if (style) {
      return '<span style="' + style + '">' + html + '</span>';
    } else {
      return html;
    }
  });

  return text.replace(/\u001B\[.*?[A-Za-z]/g, '');
}
</script>

{{ $file := .Get 0 }}
{{ $ipynb := getJSON "content/" $file }}

{{ range $cell := $ipynb.cells }}
  {{ if eq $cell.cell_type "markdown" }}
    
    {{ range $source := $cell.source }}
      {{ $.Scratch.Add "markdown" $source }}
      {{ $.Scratch.Add "content" $source }}
    {{ end }}
    {{ $.Scratch.Get "markdown" | markdownify }}
    {{ $.Scratch.Set "markdown" "" }}

    {{ range $name, $attachment := $cell.attachments }}
      {{ range $mime, $data := $attachment }}
        {{ $.Scratch.Add "attachments" (slice (dict "filename" (printf "attachment:%s" $name) "dataURL" (printf "data:%s;base64,%s" $mime $data))) }}
      {{ end }}
    {{ end }}

  {{ else if eq $cell.cell_type "code" }}
    <div class="code_cell">
      <div class="input"  style="position: relative;">
        <div class="prompt input_prompt" style="position: absolute; display: inline-block;">
          <bdi>In</bdi>[{{ $cell.execution_count }}]: &nbsp;
        </div>
        <div class="inner_cell" style="padding-left: 60px;">
          {{ $.Scratch.Add "code" (printf "```%s\n" $ipynb.metadata.kernelspec.language) }}
          {{ range $source := $cell.source }}
            {{ $.Scratch.Add "code" $source }}
          {{ end }}
          {{ $.Scratch.Add "code" "\n```" }}
          {{ $.Scratch.Get "code" | markdownify }}
          {{ $.Scratch.Set "code" "" }}
        </div>
      </div>

      {{ if $cell.outputs }}
        <span style="display: block;position: absolute;left: 86px;margin-top: -13px;background-color: white;padding: 0px 0px 0px 9px;">执行结果：</span>
        <div class="outputs" style="margin-left: 60px; border: 1px solid lightgrey; padding: 20px 0 0 20px;">
          {{ range $output := $cell.outputs }}
            {{ if eq $output.output_type "stream" }}
              {{ range $text := $output.text }}
                {{ $.Scratch.Add "outputs" $text }}
              {{ end }}
            {{ else if eq $output.output_type "error" }}
              {{ range $text := $output.traceback }}
                {{ $.Scratch.Add "outputs" $text }}
              {{ end }}
            {{ else if eq $output.output_type "execute_result" }}
              {{ range $key, $results := $output.data }}
                {{ range $text := $results }}
                  {{ $.Scratch.Add "outputs" $text }}
                {{ end }}
              {{ end }}
            {{ else }}
              {{ errorf "%s: unsupported output_type" $output.output_type }}
            {{ end }}
          {{ end }}
          <textarea >{{ $.Scratch.Get "outputs" }}</textarea>
          {{ $.Scratch.Set "outputs" "" }}
        </div>
        {{ end }}
      </div>
  {{ end }}
{{ end }}
<script type="text/javascript">
  $(".outputs>textarea").each(function(){
    var el = $(this);
    console.log(el)
    el.parent().html(function (text) {
      return term2html(text
                          .replace(/\n/g,'br-and-br')
                          .replace(/ /g, 'sp-and-sp'))
                    .replace(/(br-and-br)/g,'<br/>')
                    .replace(/(sp-and-sp)/g, '&nbsp;')
                 ;
    }(el.val()));
  })
</script>
{{ range $attachment := $.Scratch.Get "attachments" }}
  {{ $.Scratch.Set "content" (replace ($.Scratch.Get "content") $attachment.filename $attachment.dataURL) }}
{{ end }}
